package service

import (
	"context"
	"gameserver/gcommon"
	"gameserver/server/DBserver/internal"
	"gameserver/server/DBserver/logic"
	"sync"

	"gitee.com/night-tc/gobige/dblink"
	"gitee.com/night-tc/gobige/dbmanager"
	"gitee.com/night-tc/gobige/service"
)

var _ internal.IService = &DBService{}

/*
DBServer的服务接口
*/
type DBService struct {
	*service.Service
	//数据库管理器
	MysqlMgr *dblink.MysqlAccess
	//服务器信息
	Sinfo *gcommon.ServiceConfig

	playerList sync.Map
}

func NewService(sinfo *gcommon.ServiceConfig) (result *DBService) {
	result = &DBService{}
	result.Sinfo = sinfo
	result.Service = service.NewService(sinfo.GlobalConfig, result)
	result.MysqlMgr = dblink.NewMysqlAccess(dblink.SetMysqlAccessByConfig(&sinfo.MysqlConfig))
	return
}
func (this *DBService) Init() (err error) {
	this.RegMsgProc(&Service_Proc{this})
	if this.Sinfo.IsCreateDB {
		if err = this.autoMigrate(); err != nil {
			return err
		}
	}
	if err = this.Service.Init(); err != nil {
		return err
	}

	return nil
}

func (this *DBService) Destroy() {
	/*
		结束所有玩家的协程，保存数据
		开新协程完成操作，可以并发所有玩家的保存操作
		协程结束后，删除玩家对象
		但本协程还是会等所有协程结束后才会返回
	*/
	this.playerList.Range(func(k, v interface{}) bool {
		if player, ok := v.(*logic.Player); ok {
			this.GetThreadGo().Go(func(ctx context.Context) {
				player.Stop()
				this.playerList.Delete(player.GetPlayerID())
			})
		}
		return true
	})
	this.Service.Destroy()
}

/*
OnServerSessDisconnect 处理服务器会话断开事件
参数:
- sid: 服务器会话唯一标识符(uint64)，用于标识断开的服务器连接

功能说明:
1. 创建新的数据保存消息对象
2. 遍历所有已连接的玩家实例：
  - 对每个玩家对象添加数据保存消息
  - 异步触发玩家数据持久化操作

3. 该方法在服务器会话异常断开时被调用，确保玩家数据不会丢失
*/
func (this *DBService) OnServerSessDisconnect(sid uint64) {
	msg := new(logic.SaveMessage)
	this.playerList.Range(func(k, v interface{}) bool {
		if player, ok := v.(*logic.Player); ok {
			player.AddMessage(msg)
		}
		return true
	})
}

//------------------------------------------------------------------------

func (this *DBService) autoMigrate() error {
	tli := dbmanager.GetDBSingleton().GetTableList()
	otli := dbmanager.GetDBSingleton().GetOtherTableList()
	li := make([]any, 0, len(tli)+len(otli))
	for _, f := range tli {
		li = append(li, f())
	}
	return this.MysqlMgr.AutoMigrate(li...)
}

//------------------------------------------------------------------------

/*
获取玩家对象
参数:
- playerID: 玩家唯一标识符(uint64)，用于获取对应的玩家对象
返回值:
- *logic.Player: 返回对应的玩家对象指针，如果不存在则创建新的玩家对象并返回
功能说明:
1. 检查玩家对象是否已存在于内存中
2. 如果存在，则直接返回该玩家对象
3. 如果不存在，则创建新的玩家对象并返回
4. 使用sync.Map来实现线程安全的存储和获取玩家对象
5. 创建新的玩家对象时，使用sync.Once来确保线程安全
*/
func (this *DBService) GetPlayer(playerID uint64) *logic.Player {
	if v, ok := this.playerList.Load(playerID); ok {
		return v.(*logic.Player)
	} else {
		player := logic.NewPlayer(playerID, this.MysqlMgr, this.unLoadPlayer)
		this.playerList.Store(playerID, player)
		player.Start(this.GetThreadGo())
		return player
	}
}

func (this *DBService) unLoadPlayer(player *logic.Player) {
	this.CallDelay(func() {
		if player.IsTimeout() {
			player.Stop()
			this.playerList.Delete(player.GetPlayerID())
		}
	})
}
